/*-------------------------------------------------------------------------
 - Copyright (c) 2024-2025 [XD-AMCC TEAM]
 - [XD-AMCC] is licensed under Mulan PSL v2.
 - You can use this software according to the terms and conditions of the Mulan PSL v2.
 - You may obtain a copy of Mulan PSL v2 at:
 -          http://license.coscl.org.cn/MulanPSL2
 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
 - OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 - TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 - See the Mulan PSL v2 for more details.
 ------------------------------------------------------------------------*/


/**
 * @brief Implement the class token.
 *
 * @author  WXQ#XDU
 * @date    2024.11
 */

#include <assert.h>
#include <stdio.h>   // for snprintf()
#include <stdlib.h>  // for bsearch()
#include <string.h>
#include <math.h>

#include "inside/_lex_inner.h"

//------------------------------------------------------------------------
// Part I: public API for read attributes of a token object
//

EnumTokenKind token_get_kind (t_token_ptr pThis) {
    return (NULL == pThis) ? TK_UNKNOWN : pThis->tokenBody->kind;
}

const char * token_get_kindName (t_token_ptr pThis) {
    return token_nameOfKind(token_get_kind(pThis));
}

static t_position zeroPosition;
t_position_ptr token_zero_position() {
    return &zeroPosition;
}
t_position_ptr token_get_position (t_token_ptr pThis) {
    return ((NULL != pThis) ? &(pThis->pos) : &zeroPosition);
}

const char* token_get_text (t_token_ptr pThis) {
    return (NULL == pThis) ? NULL : string_text(pThis->tokenBody->inputText) ;
}

int token_get_length (t_token_ptr pThis) {
    return (NULL == pThis) ? 0 : (int)string_length(pThis->tokenBody->inputText);
}

bool token_is_type(t_token_ptr pThis) {
    EnumTokenKind kind = token_get_kind(pThis);
    return  (TK_TYPE_MIN__ < kind && kind < TK_TYPE_MAX__);
}

// Whether this token is the EOF
bool token_is_eof (t_token_ptr pThis) {
    return TK_EOF == token_get_kind(pThis);
}

token_intvalue_t  token_get_intValue (t_token_ptr pThis) {
    if(NULL == pThis || NULL == pThis->tokenBody) return 0;
    switch ( pThis->tokenBody->kind ) {
    case TK_REAL_LITERAL:
        // WARNING: conversion a double to int may cause NARROWing the value
        return (token_intvalue_t)floor(pThis->tokenBody->value.doubleValue);
    case TK_INT_LITERAL:
        return pThis->tokenBody->value.intValue;

    default:
        // For other tokens, this field hold the INT-value of token-kind
        // See token_fill_value() below
        return pThis->tokenBody->value.intValue;
    }
}

double token_get_doubleValue (t_token_ptr pThis){
    if(NULL == pThis) return 0;
    switch ( pThis->tokenBody->kind ){
    case TK_CHAR_LITERAL:
    case TK_INT_LITERAL:
        return (double)(pThis->tokenBody->value.intValue);
    case TK_REAL_LITERAL:
        return pThis->tokenBody->value.doubleValue;
    default:
        return 0;
    }
}

const char * token_get_stringValue (t_token_ptr pThis) {
    if (NULL == pThis) return NULL;
    //  just return the input-text
    return string_text(pThis->tokenBody->inputText);
}


#define STR2INT_RETURN(base)    \
    do {   pThis->kind = TK_INT_LITERAL; \
        long long  longValue = strtoll(             \
            string_text(pThis->inputText), NULL, base );  \
        if(errno == ERANGE || longValue > AMCC_INTMAX_LITERAL || longValue < 0) { \
          pThis->value.intValue = 1234567890; \
          return -1;  \
        } else  {     \
          pThis->value.intValue =                     \
            (token_intvalue_t)(AMCC_INTMAX_LITERAL & longValue); \
          return 0;         \
        }                   \
    } while(0)



int token_fill_value (t_token_body * pThis) {
    assert(NULL != pThis) ;
    errno = 0;  // clear system error
    switch ( pThis->kind ){
        case TK_INT_LITERAL_BIN:
        {   long long value = 0;
            pThis->kind = TK_INT_LITERAL;
            const char * p = string_text(pThis->inputText) + 2;
            for ( ; '\0' != *p; ++ p) {
                value = (value << 1) + (*p - '0');
                if(value > AMCC_INTMAX_LITERAL || value < 0) {
                    pThis->value.intValue = 1234567890;
                    return -1;
                }
            }
            pThis->value.intValue = (token_intvalue_t)(AMCC_INTMAX_LITERAL & value);
            return 0;
        }
            // break;  // NOT REACHED
        case TK_INT_LITERAL_DEC:    STR2INT_RETURN(10);
            // break;  // NOT REACHED
        case TK_INT_LITERAL_OCT:    STR2INT_RETURN(8);
            // break;  // NOT REACHED
        case TK_INT_LITERAL_HEX:    STR2INT_RETURN(16);
            // break;  // NOT REACHED
        case TK_REAL_LITERAL:
        {   double value = strtod(pThis->inputText, NULL);
            if (errno == ERANGE /*|| value < 0.1E-6*/ ) {
                pThis->value.doubleValue = 1.23456789 ;
                return -1;
            }
            pThis->value.doubleValue = value;
            return 0;
        }
            // break;  // NOT REACHED
        case TK_CHAR_LITERAL:
        {   int intValue = charLiteral2Int(pThis->inputText);
            if (intValue < 0 || intValue > 0xFF) {
                pThis->value.intValue = 0xFF;
                return -1;
            } else {
                pThis->value.intValue = intValue;
                return 0;
            }
        }
            // break;  // NOT REACHED
        default:
            // memset(&(pThis->value), 0, sizeof(pThis->value));
            pThis->value.intValue = pThis->kind;
            return 0;
    }

}
